//----------------------------------------------------------------------
// Class:	CGUIRoot
// Authors:	LiXizhi, Liu Weili
// Date: 2005.8.1, revised 2009.11.25
//
// desc: 
// When rendering scene, root scene and the root GUI are rendered in sequence.  
// We see that root scene is still the dominant entity in the ParaEngine. 
// 
// note:
// This is a new version of CGUIRoot. We have rewrite the class completely. The 
// class acts as the basic container and controller of the GUI system. It's responsible 
// for managing keyboard and mouse, dispatching keyboard and mouse and other events to 
// where they should be, helping the script writer to manipulating the GUI objects. 
// 
// call AdvanceGUI() to render the GUI
// @changes 2006.8 LXZ: GetDefaultObject()
// @changes 2006.9.6 LXZ: Update3DObject() is implemented. AttachTo3D() is re-implemented.
//-----------------------------------------------------------------------
/** Code Review written by LiXizhi:

---+++ Overview
Each GUIBase Object is associated with a GUIEvent(m_event) object.
m_event object contains the event states, attributes and event binding(such as mapping certain key or mouse to a standard event)

Message dispatching in GUI systems goes from parent (root) to children, where the leaf nodes has high priority to handle event than its parent.
All raw messages are actually generated by the GUIRoot object, by reading directX input during each frame move, and dispatched by the root.
GUI object and the associated m_event object are passive entities waiting for raw message to be arrived.
The raw message contains {
UINT        message;
WPARAM      wParam;
LPARAM      lParam;
DWORD       time;
POINT       pt;
}

Whenever a raw MSG (event) arrives at a given GUI object, it just calls ::MsgProc, which only process for the object itself(not children).
MsgProc is a virtual function, but it generally does the following.
- It first intepretes the raw message(event) to one Event_mapping event, according to its m_event status and event binding. Such as if two raw clicks are received, a double click event is generated.
- It processes the interpreted event (one of the Event_mapping), usually via a switch struct.
- It returns true if the event is processed, or false if the event is irrelavent to itself.

---+++ About UI Mouse Focus
Each container type object stores a mouse focus object, which can be NULL or one of its children.
The GUIRoot object contains the top level mouse focus object.
*/
#include "ParaEngine.h"

#ifdef USE_DIRECTX_RENDERER
#include "DirectXEngine.h"
#if !defined(NPLRUNTIME)
#include "GUIWebBrowser.h"
#endif
#endif
#include "SceneObject.h"
#include "AutoCamera.h"
#include "ParaWorldAsset.h"
#include "IParaEngineApp.h"
#include "BaseObject.h"		// include base class of 3D scene object
#include "ViewportManager.h"
#include "ParaEngineSettings.h"
#include "GUIScript.h"
#include "GUIEvent.h"
#include "GUIDirectInput.h"
#include "GUIText.h"
#include "GUIScrollBar.h"
#include "GUIButton.h"
#include "GUIListBox.h"
#include "GUISlider.h"
#include "GUIHighlight.h"
#include "GUIEdit.h"
#include "GUIIMEEditBox.h"
#include "EventBinding.h"
#include "EventHandler.h"
#include "ObjectManager.h"
#include "TouchEventSession.h"
#include "ic/ICConfigManager.h"
#include "PaintEngine/PaintEngineGPU.h"
#include "PaintEngine/Painter.h"
#include "EventsCenter.h"
#include "TouchSessions.h"
#include "TouchGesturePinch.h"
#include "GUIRoot.h"
#include "memdebug.h"

using namespace ParaEngine;

/**@def how many top level controls can there be in the GUI */
#define MAX_TOPLEVEL_CONTAINER_COUNT	4

#include <queue>

DWORD CGUIRoot::WheelScrollLines = 3;
DWORD CGUIRoot::KeyDelay = 200;
DWORD CGUIRoot::KeyRepeat = 30;
DWORD CGUIRoot::MouseHover = 200;
int CGUIRoot::MouseThreshold1 = 1000;
int CGUIRoot::MouseThreshold2 = 1000;
DWORD CGUIRoot::MouseAcceleration = 0;
float CGUIRoot::MouseSpeed = 1;
DWORD CGUIRoot::MouseDelay = 330;
DWORD CGUIRoot::MouseRepeat = 33;
const IType* CGUIRoot::m_type = NULL;

bool s_bIMEKeyBoardUpdated = false;

//sorter for the events
struct LessMSGCompare
{
	bool operator()(
		const MSG _Left,
		const MSG _Right
		) const
	{
		if ((_Left.time) < (_Right.time))
			return true;
		else
			return false;
	};
};
/* ======================================================================
for CGUIRoot
======================================================================*/
CGUIRoot::CGUIRoot(void)
	: engine(NULL),
	m_fUIScalingX(1.f), m_fUIScalingY(1.0f), m_fViewportLeft(0.f), m_fViewportTop(0.f), m_fViewportWidth(0.f), m_fViewportHeight(0.f),
	m_bMouseInClient(true), m_nLastTouchX(-1000), m_nLastTouchY(-1000), m_bIsNonClient(false),
	m_fMinScreenWidth(400.f), m_fMinScreenHeight(300.f), m_bHasIMEFocus(false), m_bIsCursorClipped(false), m_nFingerSizePixels(60), m_nFingerStepSizePixels(10), m_pActiveWindow(NULL), m_pLastMouseDownObject(NULL), m_bMouseCaptured(false)
{
	if (!m_type){
		m_type = IType::GetType("guiroot");
	}
	m_sIdentifer = "__root";
#ifdef USE_DIRECTX_RENDERER
	m_stateGUI.d3dBackbufferFormat = D3DFMT_X8R8G8B8;
#endif
	m_pKeyboard = NULL;
	m_pMouse = NULL;

	m_IMEFocus = NULL;
	m_bActive = true;
	m_bDesign = false;
	m_bReceiveDrag = true;
	m_bRenderImageCursor = false;
	m_bUseSystemCursor = false;

	m_bWindowChanging = false;
	//m_oldcursor=::GetCursor();
	CUniBuffer::Initialize();
	// CGUIIMEEditBox::Initialize();
	if (!m_objResource) {
		m_objResource = new CGUIResource();
	}
	m_objResource->SetActiveLayer();
	m_objResource->SetCurrentState();
	GUITextureElement temp;
	m_objResource->AddElement(&temp);
	m_objResource->AddElement(&temp);
	m_tooltip = NULL;

	//the following lines is just to make sure the program works.
	CObjectManager* pOm = &CSingleton<CObjectManager>::Instance();
	if (!pOm->IsExist("default_CEventBinding")) {
		CEventBinding::StaticInit();
	}
	SAFE_RELEASE(m_event);
	m_event = new CGUIEvent();
	m_event->SetBinding(this);
	m_event->m_eventbinding = EventBinding_cow_type(new CEventBinding(*((CEventBinding*)pOm->GetObject("default_CEventBinding"))));
	CEventBinding* pBinding = m_event->GetEventBindingObj();
	pBinding->MapEvent(EM_MOUSE_LEFTDOWN, EM_CTRL_FOCUSIN);
	pBinding->MapEvent(EM_MOUSE_RIGHTDOWN, EM_CTRL_FOCUSIN);
	pBinding->MapEvent(EM_MOUSE_MIDDLEDOWN, EM_CTRL_FOCUSIN);
	pBinding->MapEvent(EM_MOUSE_WHEEL, EM_CTRL_FOCUSIN);

	m_TopLevelCtrls.resize(MAX_TOPLEVEL_CONTAINER_COUNT, NULL);
	m_nID = 0;

	// auxiliary painter mostly for calculating text size from scripting interface. 
	m_pPainter = new CPainter();
	m_pPainter->SetHelperDevice(this);
	GetGUIState().painter = m_pPainter;

	// install all gestures
	TouchSessions::GetInstance().CleanupAllGestures();
	auto gesture_pinch = new CTouchGesturePinch();
	gesture_pinch->handleGestureRecognized = std::bind(&CGUIRoot::handleGesturePinch, this, std::placeholders::_1);
	TouchSessions::GetInstance().RegisterGestureRecognizer(gesture_pinch);
}

CGUIRoot::~CGUIRoot(void)
{
	SAFE_DELETE(m_pPainter);
	SAFE_DELETE(engine);
	CGUIIMEEditBox::Uninitialize();
	CUniBuffer::Uninitialize();
	TouchSessions::GetInstance().ClearTouchSessions();
}

//no need refactoring
void CGUIRoot::Initialize()
{
	Clear();
	m_tooltip = new CGUIToolTip();
	m_tooltip->Initialize();
	m_tooltip->InitObject("__tooltip", "_lt", 0, 0, 0, 0);
#ifdef WIN32
	int temp2;
	SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &temp2, 0);
	CGUIRoot::KeyDelay = (temp2 + 1) * 250;
	SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &temp2, 0);
	CGUIRoot::KeyRepeat = 33 + 12 * (31 - temp2);
	SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &CGUIRoot::WheelScrollLines, 0);//not in Windows 95
	int temp1[3];
	SystemParametersInfo(SPI_GETMOUSE, 0, temp1, 0);
	CGUIRoot::MouseThreshold1 = temp1[0];
	CGUIRoot::MouseThreshold2 = temp1[1];
	CGUIRoot::MouseAcceleration = temp1[2];
	SystemParametersInfo(SPI_GETMOUSEHOVERTIME, 0, &temp2, 0);//not in Windows 95
	CGUIRoot::MouseHover = temp2;
	SystemParametersInfo(SPI_GETMOUSESPEED, 0, &temp2, 0);//not in Windows NT and Windows 95
	CGUIRoot::MouseSpeed = ((float)temp2) / 10;
#endif
}
// clean up all UI objects. 
void CGUIRoot::Clear()
{
	TouchSessions::GetInstance().ClearTouchSessions();
	
	DestroyChildren();
	SAFE_RELEASE(m_tooltip);
	ClearAllTopLevelControls();

	if (!m_idmap.empty())
	{
		OUTPUT_LOG("%d UI object is not deleted when GUI is reset\n", (int)m_idmap.size());
	}

	// m_scripts.init();
	// Fixed LiXizhi: instead of clear all scripts, we will clear all scripts except the ondestroy script, so that they are called as expected. 
	int nSize = (int)m_scripts.size();
	if (nSize > 0)
	{
		for (int i = 0; i < nSize; i++)
		{
			GUI_SCRIPT& sc = CGlobals::GetGUI()->m_scripts[i];
			sc.m_bDelete = (sc.m_objType != EM_WM_DESTROY);
		}
		m_scripts.DeleteMarkedScript();
	}
}

void CGUIRoot::ActivateRoot()
{
#if defined(USE_DIRECTX_RENDERER)|| 0
	if (!m_bActive && m_pMouse && m_pKeyboard) 
	{
		if (m_pMouse->m_pMouse)
		{
			m_pMouse->m_pMouse->Acquire();
		}
		if (m_pKeyboard->m_pKeyboard)
		{
			m_pKeyboard->m_pKeyboard->Acquire();
		}

		m_pMouse->m_dwElements=0;
		m_pKeyboard->SetElementsCount(0);
		m_bActive=true;
		//UseDefaultMouseCursor(false);
		UpdateCursorPosition();
		if(m_bActive)
		{
			m_pMouse->ShowCursor(true);
			m_pMouse->SetLock(false);
		}
	}
#endif
}

void CGUIRoot::InactivateRoot()
{
#if defined(USE_DIRECTX_RENDERER) || 0
	if (m_bActive && m_pMouse && m_pKeyboard) 
	{
		m_pMouse->m_dwElements=0;
		m_pKeyboard->SetElementsCount(0);
		if (m_pMouse->m_pMouse)
		{
			m_pMouse->m_pMouse->Unacquire();
		}
		if (m_pKeyboard->m_pKeyboard)
		{
			m_pKeyboard->m_pKeyboard->Unacquire();
		}
		m_bActive=false;
		Reset();
		//UseDefaultMouseCursor(true);
		POINT ptCursor;
		ptCursor.x=m_pMouse->m_x;
		ptCursor.y=m_pMouse->m_y;
		//::ClientToScreen(CGlobals::GetAppHWND(), &ptCursor);
		//::SetCursorPos(ptCursor.x,ptCursor.y);
	}
#endif
}

string CGUIRoot::ToScript(int option)
{
	string script = "";
	if (!m_bDesign) {
		//add default scripts
		script += "local function activate(intensity)\n";
		script += "local __this,__parent,__res1,__res2;\n";
	}

	queue<CGUIBase*> queueNodes;
	queueNodes.push((CGUIBase*)this);
	/// -breadth first transversing the scene
	while (!queueNodes.empty())
	{
		CGUIBase* pObj = queueNodes.front();
		queueNodes.pop();

		//if a control starts with "__", it is the IDE control. we will skip them.
		if ((!(pObj->GetName()[0] == '_'&&pObj->GetName()[1] == '_') || !m_bDesign) && pObj->GetType()->GetTypeValue() == Type_GUIRoot) {
			script += pObj->ToScript(option);
		}
		//check if the object is a container, if not, skip search for its children
		if (!((CGUIType*)pObj->GetType())->IsContainer()) {
			continue;
		}
		// search all children
		GUIBase_List_Type::iterator itCurCP, itEndCP = ((CGUIContainer*)pObj)->GetChildren()->end();
		for (itCurCP = ((CGUIContainer*)pObj)->GetChildren()->begin(); itCurCP != itEndCP;)
		{
			CGUIBase* pObjChild = *itCurCP;
			queueNodes.push(pObjChild);
			++itCurCP;
		}
	}
	if (!m_bDesign) {
		//add default endings
		script += "end\n";
		script += "NPL.this(activate);\n";
	}
	return script;
}

//no need refactoring
void CGUIRoot::DeleteGUIObject(CGUIBase * pObj)
{
	if (!pObj)
		return;
	CGUIRoot *root = CGUIRoot::GetInstance();
	
	if(root->m_tooltip)
		root->m_tooltip->RemoveTip(pObj);
	if(root->m_pMouse)
		root->m_pMouse->ReleaseCapture(pObj);
	pObj->InvalidateDeviceObjects();
	pObj->DeleteDeviceObjects();

	if (pObj->m_parent)
	{
		pObj->m_parent->UpdateClientRect(*pObj->GetPosition(), true);
		CGUIBase* keyfocus = NULL;
		CGUIBase* mousefocus = NULL;
		if (pObj->m_parent) {
			keyfocus = pObj->m_parent->GetKeyFocus();
			mousefocus = pObj->m_parent->GetMouseFocus();
		}

		if (pObj == keyfocus&&pObj->m_parent) {
			pObj->m_parent->SetKeyFocus(NULL);
		}
		if (pObj == mousefocus&&pObj->m_parent) {
			pObj->m_parent->SetMouseFocus(NULL);
		}
	}
	if (pObj == root->m_pActiveWindow)
		root->m_pActiveWindow = NULL;
	SAFE_RELEASE(pObj);
}
void CGUIRoot::PostDeleteGUIObject(CGUIBase* pobj)
{
	if (pobj){
		CGUIRoot::GetInstance()->m_deleteQueue.push_back(pobj->GetID());
	}
}

//---------------------------------------------------------------------------------
/// pParent can be NULL, the root GUI will be used then
//---------------------------------------------------------------------------------
//no need refactoring
void CGUIRoot::AttachGUIElement(CGUIBase* pParent, CGUIBase* pChild)
{
	if (pParent == NULL)
		pParent = (CGUIBase*)this;
	if (!((CGUIType*)pParent->GetType())->IsContainer()) {
		return;
	}
	if (pChild)
	{
		if (pChild->m_parent != NULL){
			DetachGUIElement(pChild);
		}
		if (pParent->GetType()->GetTypeValue() != Type_GUIRoot&&pChild->GetType()->GetTypeValue() == Type_GUIContainer){
			((CGUIContainer*)pChild)->SetTopLevel(false);
		}
		CGUIContainer* parent = ((CGUIContainer*)pParent);
		parent->AddChild(pChild);
		const CGUIPosition *childPos = pChild->GetPosition();

		parent->UpdateClientRect(*childPos);

		pChild->m_parent = (CGUIContainer*)pParent;
		pChild->m_bNeedUpdate = true;
		pChild->UpdateRects();
	}
}

//no need refactoring
void CGUIRoot::DetachGUIElement(CGUIBase* pChild)
{
	if (!pChild) return;
	if (pChild->m_parent) {
		if (pChild->m_parent->GetMouseFocus() == pChild) {
			pChild->m_parent->SetMouseFocus(NULL);
		}
		if (pChild->m_parent->GetKeyFocus() == pChild) {
			pChild->m_parent->SetKeyFocus(NULL);
		}
		//updates the parent's childrect
		pChild->m_parent->UpdateClientRect(*pChild->GetPosition(), true);
		GUIBase_List_Type::iterator iter, iterEnd = pChild->m_parent->GetChildren()->end();
		for (iter = pChild->m_parent->GetChildren()->begin(); iter != iterEnd; iter++) {
			if ((*iter) == pChild) {
				iter = pChild->m_parent->GetChildren()->erase(iter);
				break;
			}
		}
		pChild->m_parent->m_bNeedUpdate = true;
		pChild->m_bNeedUpdate = true;
		pChild->m_parent = NULL;
	}
}

//no need refactoring
int CGUIRoot::Release()
{
	Clear();
	SAFE_DELETE(m_pKeyboard);
	SAFE_DELETE(m_pMouse);

	// this is tricky: since all other CGUIBase object's release is equal to delete this, except the root one. 
	SAFE_RELEASE(m_objResource);
	SAFE_RELEASE(m_event);
	return 0;
}

//no need refactoring
CGUIBase* CGUIRoot::GetUIObject(const char * strObjectName)
{
	if (strObjectName == 0 || strObjectName[0] == 0)
		return NULL;
	if (strcmp(strObjectName, "root") == 0)
		return this;

	map<string, CGUIBase*>::iterator iter = m_namemap.find(strObjectName);
	if (iter == m_namemap.end()) {
		return NULL;
	}
	return iter->second;
}

CGUIBase* CGUIRoot::GetUIObject(const std::string& strObjectName)
{
	auto iter = m_namemap.find(strObjectName);
	return (iter != m_namemap.end()) ? iter->second : NULL;
}


CGUIBase* ParaEngine::CGUIRoot::GetUIObject(int nID)
{
	if (nID > 0)
	{
		map<int, CGUIBase*>::iterator iter = m_idmap.find(nID);
		if (iter == m_idmap.end()) {
			return NULL;
		}
		return iter->second;
	}
	else if (nID == 0)
	{ // return root if id is 0.
		return this;
	}
	else
	{
		//OUTPUT_LOG("warning: CGUIRoot::GetUIObject is called with an invalid  id\n");
		return NULL;
	}
}

void CGUIRoot::PushTopLevelControl(CGUIContainer* pTopLevelControl)
{
	if (pTopLevelControl == 0)
		return;
	bool bExist = false;
	int nCount = (int)m_TopLevelCtrls.size();
	for (int i = 0; i < nCount; ++i)
	{
		if (m_TopLevelCtrls[i] == pTopLevelControl)
		{
			// if pTopLevelControl is already a top level control, shift it to level 0.
			for (int j = i; j >= 1; --j)
			{
				m_TopLevelCtrls[j] = m_TopLevelCtrls[j - 1];
			}
			bExist = true;
		}
	}
	if (!bExist)
	{
		// if pTopLevelControl is not a top level control, push it to level 0 and pop the last one if any.
		for (int j = nCount - 1; j >= 1; --j)
		{
			m_TopLevelCtrls[j] = m_TopLevelCtrls[j - 1];
		}
	}
	// make it top level any way
	m_TopLevelCtrls[0] = pTopLevelControl;
	((CGUIBase*)pTopLevelControl)->BringToFront();
}

void CGUIRoot::RemoveTopLevelControl(CGUIContainer* pTopLevelControl)
{
	int nCount = (int)(m_TopLevelCtrls.size());
	for (int i = 0; i < nCount; ++i)
	{
		if (m_TopLevelCtrls[i] == pTopLevelControl)
		{
			// if pTopLevelControl is already a top level control, move every control below it one level upward and fill the last level with 0.
			for (int j = i; j < (nCount - 1); ++j)
			{
				m_TopLevelCtrls[j] = m_TopLevelCtrls[j + 1];
			}
			m_TopLevelCtrls[nCount - 1] = NULL;
			if (i == 0 && m_TopLevelCtrls[0] != NULL)
			{
				// bring top level control to front. if it is the new level 0 control.
				((CGUIBase*)m_TopLevelCtrls[0])->BringToFront();
			}
		}
	}
}
void CGUIRoot::ClearAllTopLevelControls()
{
	int nCount = (int)(m_TopLevelCtrls.size());
	for (int i = 0; i < nCount; ++i)
	{
		m_TopLevelCtrls[i] = NULL;
	}
}

/**
* return the most top level(leaf) object that contains the mouse point(x,y).
* top level control is returned regardless of whether it contains the point or not. However, the exact leaf child of top level control that contains the point will be returned.
* it never returns GUIRoot object. It return NULL if mouse is not over any GUI object.
*/
CGUIBase* CGUIRoot::GetUIObject(int x, int y)
{
	CGUIBase* pDest = this;
	CGUIContainer* TopCtrl = GetTopLevelControl();
	if (TopCtrl)
	{
		if (TopCtrl->IsPointInControl(x, y))
		{
			pDest = TopCtrl;
		}
		else
		{
			return TopCtrl; // 2007.4.1 LXZ: this will allow the top level control to always receive mouse message even outside its client area. In most cases, one should always handle mouse up message to decide whether it is a click outside the client area of a top level control.
		}
	}
	pDest = ((CGUIContainer*)pDest)->GetObjectAtPointRecursive(x, y);

	if (pDest != 0 && pDest->GetType()->GetTypeValue() == Type_GUIRoot)
	{
		pDest = NULL;
	}
	return pDest;
}

//----------------------------------------------------------------------------
/// desc: destroy object and all its decedents from the GUI.
//----------------------------------------------------------------------------
//no need refactoring
int CGUIRoot::DestroyGUIElement(CGUIBase* obj)
{
	if (obj == 0)
		return -1;
	// first detach, and then delete the detached object. 
	DetachGUIElement(obj);

	if (((CGUIType*)obj->GetType())->IsContainer()) {
		((CGUIContainer*)obj)->DestroyChildren();
	}
	DeleteGUIObject(obj);
	return 0;
}

//----------------------------------------------------------------------------
// desc: 
/// given a id name, destroy it and all its descendants from the GUI.
/// there may be several object who has the same id, then only the last attached is deleted
//----------------------------------------------------------------------------
//no need refactoring
int CGUIRoot::DestroyGUIElement(const char* sID)
{
	CGUIBase* pObj = GetUIObject(sID);
	if (pObj != 0 && pObj != this)
	{
		return DestroyGUIElement(pObj);
	}
#ifdef _DEBUG
	if (sID)
	{
		OUTPUT_LOG("warning:failed DestroyGUIElement %s\n", sID);
	}
#endif

	return -1;
}

//no need refactoring
HRESULT CGUIRoot::Render(GUIState* pGUIState, float fElapsedTime)
{
	return S_OK;
}

void CGUIRoot::Update3DObject(float fElapsedTime)
{
	CSceneObject* pScene = CGlobals::GetScene();
	CAutoCamera* pCamera = ((CAutoCamera*)(CGlobals::GetScene()->GetCurrentCamera()));
	GUIBase_List_Type::iterator iter, itEnd = GetChildren()->end();

	for (iter = GetChildren()->begin(); iter != itEnd; ++iter)
	{
		CGUIBase* pObjChild = *iter;

		if (pObjChild->GetPosition()->Is3DObjectSpecified())
		{
			CBaseObject * p3DObject = pObjChild->GetPosition()->GetRelative3DObject(pScene);
			bool bVisible = false;
			if (p3DObject != 0 && p3DObject->IsVisible())
			{
				Vector3 vPos(p3DObject->GetRenderOffset());
				vPos.y += p3DObject->GetHeight();

				if (pCamera->GetObjectFrustum()->CullPointsWithFrustum(&vPos, 1))
				{
					memcpy(pObjChild->GetPosition()->Relative.To3D.m_v3DPosition, &vPos, sizeof(Vector3));
					bVisible = true;
				}
			}
			pObjChild->SetVisible(bVisible);
			if (!bVisible)
			{
				// fixed by LiXizhi: invisible 3d object will also frame move
				pObjChild->OnFrameMove(fElapsedTime);
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Name: AdvanceGUI()
/// Desc: Advances the local animation time by dTimeDelta, and render it
/// call this function in Render of the parent window to draw the entire 
/// GUI. 
/// [Note]: m_stateGUI struct must be filled carefully before calling this class
//-----------------------------------------------------------------------------
//no need refactoring
void	CGUIRoot::AdvanceGUI(float fElapsedTime)
{
	if (!m_bIsVisible)
		return;
	STRUCT_DRAG_AND_DROP *pdrag = &IObjectDrag::DraggingObject;

	GUIState* pGUIState = &m_stateGUI;
	/// clean up GUI state
	pGUIState->CleanupGUIState();
	RenderDevicePtr pd3dDevice = CGlobals::GetRenderDevice();
	pGUIState->pd3dDevice = pd3dDevice;

	auto painter = GetPainter();
	pGUIState->painter = painter;
	if (painter->isActive())
		painter->end();

	painter->begin(this);

	// update which 3d object are visible.
	Update3DObject(fElapsedTime);
#ifdef USE_OPENGL_RENDERER
	if (painter->isActive())
	{
		for (auto iter = GetChildren()->begin(); iter != GetChildren()->end(); iter++)
		{
			CGUIBase* pObjChild = *iter;
			/// skip any node that is not visible
			if (!(pObjChild->m_bIsVisible))
				continue;
			pObjChild->DoRender(pGUIState, fElapsedTime);
		}
		painter->Flush();

		//////////////////////////////////////////////////////////////////////////
		// render highlighting markers
		CGUIHighlightManager* gm = &CSingleton<CGUIHighlightManager>::Instance();
		gm->Render(pGUIState, fElapsedTime, true);

		//////////////////////////////////////////////////////////////////////////
		// render tooltips
		m_tooltip->Render(pGUIState, fElapsedTime);

		//////////////////////////////////////////////////////////////////////////
		//render the dragging object if exist
		if (pdrag->pDragging != NULL && !pdrag->m_bIsCandicateOnly) {
			((CGUIBase*)pdrag->pDragging)->DoRender(pGUIState, fElapsedTime);
		}
		painter->end();
	}
#endif

#ifdef USE_DIRECTX_RENDERER
	if (painter->isActive())
	{
		if(GetUsePointTextureFiltering())
		{
			CGlobals::GetEffectManager()->SetSamplerState( 0, D3DSAMP_MINFILTER,  D3DTEXF_POINT, true);
			CGlobals::GetEffectManager()->SetSamplerState( 0, D3DSAMP_MAGFILTER,  D3DTEXF_POINT, true);
		}

		// default to UV wrapping, instead of UV clamp for UI. 
		// pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU,  D3DTADDRESS_WRAP);
		// pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV,  D3DTADDRESS_WRAP );

		GUIBase_List_Type::iterator iter;

		//////////////////////////////////////////////////////////////////////////
		// breadth first transversing the 2D scene
		//pd3dDevice->SetRenderState(D3DRS_STENCILENABLE,TRUE);
		//pd3dDevice->SetRenderState(D3DRS_STENCILFUNC,D3DCMP_EQUAL);
		//pd3dDevice->SetRenderState(D3DRS_STENCILREF,0);
		for( iter = this->GetChildren()->begin(); iter != this->GetChildren()->end();iter++ )
		{
			CGUIBase* pObjChild = * iter;  
			/// skip any node that is not visible
			if(!(pObjChild->m_bIsVisible))
				continue; 
			pObjChild->DoRender(pGUIState, fElapsedTime);
		}
		painter->Flush();

		//////////////////////////////////////////////////////////////////////////
		// render highlighting markers
		CGUIHighlightManager* gm=&CSingleton<CGUIHighlightManager>::Instance();
		gm->Render(pGUIState, fElapsedTime,true);

		//////////////////////////////////////////////////////////////////////////
		// render tooltips
		m_tooltip->Render(pGUIState, fElapsedTime);

		//////////////////////////////////////////////////////////////////////////
		//render the dragging object if exist
		pd3dDevice->SetRenderState(D3DRS_STENCILENABLE,FALSE);
		if (pdrag->pDragging!=NULL && ! pdrag->m_bIsCandicateOnly) {
			((CGUIBase*)pdrag->pDragging)->DoRender(pGUIState, fElapsedTime);
		}
		//////////////////////////////////////////////////////////////////////////
		//render the IME candidate window
		pd3dDevice->SetRenderState(D3DRS_STENCILENABLE,FALSE);
		//if (GetIMEFocus()) {
		//	GetIMEFocus()->PostRender(pGUIState, fElapsedTime);
		//}
		//draw user defined cursor here
		//LXZ: draw user defined image cursor here, this is only used for movie capturing
		if(m_bRenderImageCursor)
			m_pMouse->RenderCursor(pGUIState,fElapsedTime);

		painter->end();
	}

	if(GetUsePointTextureFiltering())
	{
		CGlobals::GetEffectManager()->SetSamplerState( 0, D3DSAMP_MINFILTER,  D3DTEXF_LINEAR);
		CGlobals::GetEffectManager()->SetSamplerState( 0, D3DSAMP_MAGFILTER,  D3DTEXF_LINEAR);
	}

#if !defined(NPLRUNTIME)
	/** global frame move */
	CGUIWebBrowser::GlobalFrameMove();
#endif
#endif

	if (!pGUIState->listPostRenderingObjects.empty())
	{
		for (WeakPtr& obj : pGUIState->listPostRenderingObjects)
		{
			if (obj)
			{
				CGUIBase* pGUIObj = static_cast<CGUIBase*>(obj.get());
				if (pGUIObj->IsSelfPaintEnabled())
				{
					pGUIState->bIsBatchRender = false;
					pGUIObj->DoSelfPaint(pGUIState, fElapsedTime);
				}
			}
		}
	}
}

void VisitAllObjects(CGUIRoot& root, const std::function<void(CGUIBase* pObj)> & callbackFunc)
{
	queue<CGUIBase*> queueNodes;
	queueNodes.push((CGUIBase*)&root);
	// -breadth first transversing the scene
	while (!queueNodes.empty())
	{
		CGUIBase* pObj = queueNodes.front();
		queueNodes.pop();

		if (pObj->GetType()->GetTypeValue() != Type_GUIRoot)
		{
			callbackFunc(pObj);
		}

		//check if the object is a container, if not, skip search for its children
		if (((CGUIType*)pObj->GetType())->IsContainer()) {
			continue;
		}

		// add all children
		GUIBase_List_Type::iterator itCurCP, itEndCP = ((CGUIContainer*)pObj)->GetChildren()->end();

		for (itCurCP = ((CGUIContainer*)pObj)->GetChildren()->begin(); itCurCP != itEndCP; ++itCurCP)
		{
			queueNodes.push(*itCurCP);
		}
	}
	callbackFunc(root.m_tooltip);
}

//no need refactoring
HRESULT CGUIRoot::InitDeviceObjects()
{
	m_stateGUI.pd3dDevice = CGlobals::GetRenderDevice();

	auto f = [](CGUIBase* pObj) { pObj->InitDeviceObjects(); };
	VisitAllObjects(*this, f);

#ifdef USE_DIRECTX_RENDERER
	//updates the current display mode
	EnumDisplaySettings (NULL,ENUM_CURRENT_SETTINGS,&m_displaymode);
#endif
	return 0;
}

// device dependent
//no need refactoring
HRESULT CGUIRoot::RestoreDeviceObjects(UINT nBkbufWidth, UINT nBkbufHeight)
{
	auto f = [](CGUIBase* pObj) { pObj->RestoreDeviceObjects(); };
	VisitAllObjects(*this, f);

	UpdateRects();
	return 0;
};

//no need refactoring
HRESULT CGUIRoot::InvalidateDeviceObjects()
{
	auto f = [](CGUIBase* pObj) { pObj->InvalidateDeviceObjects(); };
	VisitAllObjects(*this, f);
	return 0;
};

//no need refactoring
HRESULT CGUIRoot::DeleteDeviceObjects()
{
	ClearPredefinedTextureElement();

	auto f = [](CGUIBase* pObj) { pObj->DeleteDeviceObjects(); };
	VisitAllObjects(*this, f);
	return 0;
}

//no need refactoring
void CGUIRoot::Reset()
{
	CDirectKeyboard *pKeyboard = CGUIRoot::GetInstance()->m_pKeyboard;
	CDirectMouse *pMouse = CGUIRoot::GetInstance()->m_pMouse;

	if (pKeyboard) {
		pKeyboard->Reset();
	}
	if (pMouse) {
		pMouse->Reset();
	}
	m_events.clear();

	auto f = [](CGUIBase* pObj) { pObj->Reset(); };
	VisitAllObjects(*this, f);
}

void CGUIRoot::GetMousePosition(int* nX, int* nY) const
{
	if (m_pMouse)
	{
		if (nX != 0)
			*nX = m_pMouse->m_x;
		if (nY != 0)
			*nY = m_pMouse->m_y;
	}
}

bool ParaEngine::CGUIRoot::IsActive()
{
	return m_bActive;
}

bool ParaEngine::CGUIRoot::IsKeyboardProcessed()
{
	return m_bKeyboardProcessed;
}

bool ParaEngine::CGUIRoot::IsMouseProcessed()
{
	return m_bMouseProcessed;
}

bool ParaEngine::CGUIRoot::IsNonClient() const
{
	return m_bIsNonClient;
}

void ParaEngine::CGUIRoot::SetIsNonClient(bool val)
{
	m_bIsNonClient = val;
}

void ParaEngine::CGUIRoot::SetMousePosition(int nX, int nY)
{
	m_pMouse->SetMousePosition(nX, nY);

#if defined(USE_DIRECTX_RENDERER) || 0
	if (m_pMouse->IsLocked())
	{
		// Set position of camera to center of desktop, 
		// so it always has room to move.  This is very useful
		// if the cursor is hidden.  If this isn't done and cursor is hidden, 
		// then invisible cursor will hit the edge of the screen 
		// and the user can't tell what happened
		POINT ptCenter;
		RECT rcDesktop;
		GetWindowRect(GetDesktopWindow(), &rcDesktop);
		ptCenter.x = (rcDesktop.right - rcDesktop.left) / 2;
		ptCenter.y = (rcDesktop.bottom - rcDesktop.top) / 2;
		SetCursorPos(ptCenter.x, ptCenter.y);
	}
#endif
}

int ParaEngine::CGUIRoot::GetFingerSizePixels() const
{
	return m_nFingerSizePixels;
}

void ParaEngine::CGUIRoot::SetFingerSizePixels(int val)
{
	m_nFingerSizePixels = val;
}

int ParaEngine::CGUIRoot::GetFingerStepSizePixels() const
{
	return m_nFingerStepSizePixels;
}

void ParaEngine::CGUIRoot::SetFingerStepSizePixels(int val)
{
	m_nFingerStepSizePixels = val;
}

// obsoleted
void ParaEngine::CGUIRoot::DispatchTouchMouseEvent(bool &bMouseHandled)
{
	MSG newMsg;
	CGUIBase* temp = NULL;
	POINT pt;
	pt.x = pt.y = 0;
	CEventBinding::InitMsg(&newMsg, GetTickCount(), EM_NONE, pt);
	CGUIContainer* TopCtrl = GetTopLevelControl();
	for (const auto& event : m_events)
	{
		CGUIBase* pMouseTarget = NULL;
		bool bIsMouseEvent = false;
		switch (event.message)
		{
		case EM_MOUSE_LEFTDOWN:
		case EM_MOUSE_LEFTUP:
		case EM_MOUSE_WHEEL:
		case EM_MOUSE_MOVE:
			pMouseTarget = GetUIObject(event.pt.x, event.pt.y);
			newMsg = event;
			bIsMouseEvent = true;
			break;
		default:
			break;
		}
		if (bIsMouseEvent)
		{
			if (pMouseTarget)
			{
				if (TopCtrl == pMouseTarget)
				{
					// if the top level container is the mouse target, let it alone to process the message. this prevents TopCtrl to send Z-order messages to its parent(usually the GUIRoot).
					// a top level container is the mouse target only if the mouse pointer does not falls into any of its children. 
					// In other word, top level container is full screen regardless of its size. 
					bMouseHandled = pMouseTarget->MsgProc(&newMsg);
				}
				else
				{
					// let the leaf mouse targeted object to handle the raw message first, if leaf does not handle it, the message is sent to its parent for processing, 
					// and so on until either some object processes it or the root or top control object is met. 
					bool bMouseTargetParentIsTopLevelCtrl = false;
					temp = pMouseTarget;
					while (temp && !(bMouseHandled |= temp->MsgProc(&newMsg))
						&& !(bMouseTargetParentIsTopLevelCtrl = (temp == TopCtrl)))
					{
						temp = temp->m_parent;
					}
					if (!bMouseTargetParentIsTopLevelCtrl)
					{
						// bring the mouse target window to front and activate it, if event is mouse down and parent is not a top level control
						if (pMouseTarget->m_event->IsMapTo(newMsg.message, EM_MOUSE_DOWN))
						{
							BringToFront(pMouseTarget);
							// call the WM_activate script. TODO: shall we call the deactivate script for the last window that loses focus. 
							// please note that this function might be called multiple times for the same window. 
							pMouseTarget->MakeActivate(1);
						}
					}
				}
			}
			else
			{
				// if mouse point is not on any GUI object, just let the GUIRoot process the message and reset mouse focus. 
				bMouseHandled = MsgProc(&newMsg);
				SetUIMouseFocus(NULL);
			}
		}
	}
	m_events.clear();
}


void ParaEngine::CGUIRoot::DestroyChildren()
{
	CGUIContainer::DestroyChildren();
	m_namemap.clear();
}

bool ParaEngine::CGUIRoot::DispatchKeyboardMsg(bool bKeyHandled)
{
#ifdef USE_DIRECTX_RENDERER
	MSG newMsg;
	POINT pt;
	pt.x = pt.y = 0;
	CEventBinding::InitMsg(&newMsg, GetTickCount(), EM_NONE, pt);

	CGUIBase* pKeyTarget = GetUIKeyFocus();
	bool bNoKeyMessage = false;
	bool bHasImeFocus = GetIMEFocus() && pKeyTarget && GetIMEFocus()->GetVisibleRecursive();
	SetHasIMEFocus(bHasImeFocus);
	std::wstring sWinMsgChar = CGUIIME::GetWinMsgChar(true);
	if (!sWinMsgChar.empty() && pKeyTarget)
	{
		pKeyTarget->OnHandleWinMsgChars(sWinMsgChar);
	}

	if (bHasImeFocus)
	{
		CGUIIME::GetLastCompString(true);
		GetIMEFocus()->FetchIMEString();
		if (!m_pKeyboard->IsUseWindowsMessage())
		{
			// when IME is used. we will do m_pKeyboard->Update() in the CGUIRoot::MsgProc
			// Since IME messages are window messages, but m_pKeyboard->Update() is hardware, we need to call m_pKeyboard->Update() 
			// as close to window message processor as possible in order to prevent hardware keys are more than software keys. 
			if (!s_bIMEKeyBoardUpdated)
			{
				// between m_pKeyboard->Update() in the CGUIRoot::MsgProc, the Dispatch function may be called multiple times. 
				// This prevents the same keyboard states to be processed multiple times during IME mode. 
				bNoKeyMessage = true;
			}
		}
		else
			m_pKeyboard->Update();
	}
	else
	{
		m_pKeyboard->Update();
	}

	IMESTATE ime_state = CGUIIME::GetImeStateS();
	if (ime_state == IMEUI_STATE_ON && !bHasImeFocus){
		CGUIIME::OnFocusOut();
	}
	else if (bHasImeFocus && !CGUIIME::IMEHasFocus())
	{
		CGUIIME::OnFocusIn();
	}

	s_bIMEKeyBoardUpdated = false;

	if (!bNoKeyMessage)
	{
		//synchronize the stored key state with the actual state
		for (int a = 0; a < 256; a++) {
			if (CEventBinding::ScancodeToKeyTable[a] != 0) {
				CGUIEvent::KeyStates[CEventBinding::ScancodeToKeyTable[a]] = m_pKeyboard->GetLastKeyState(a);
			}
		}

		if (pKeyTarget != NULL)
		{
			newMsg.message = EM_CTRL_UPDATEKEY;
			DWORD oldsize = (DWORD)(m_events.size());
			bKeyHandled = pKeyTarget->MsgProc(&newMsg);
			//TODO: temporary solution shall delete
			bKeyHandled = (oldsize == m_events.size());
		}
		else
		{
			newMsg.message = EM_CTRL_UPDATEKEY;
			bKeyHandled = false;
			MsgProc(&newMsg);
		}
	}
#elif defined(USE_OPENGL_RENDERER)
	MSG newMsg;
	POINT pt;
	pt.x = pt.y = 0;
	CEventBinding::InitMsg(&newMsg, GetTickCount(), EM_NONE, pt);

	CGUIBase* pKeyTarget = GetUIKeyFocus();

	m_pKeyboard->Update();

	s_bIMEKeyBoardUpdated = false;

	{
		//synchronize the stored key state with the actual state
		for (int a = 0; a < 256; a++) {
			if (CEventBinding::ScancodeToKeyTable[a] != 0) {
				CGUIEvent::KeyStates[CEventBinding::ScancodeToKeyTable[a]] = m_pKeyboard->GetLastKeyState(a);
			}
		}
		if (pKeyTarget != NULL)
		{
			newMsg.message = EM_CTRL_UPDATEKEY;
			DWORD oldsize = (DWORD)(m_events.size());
			bKeyHandled = pKeyTarget->MsgProc(&newMsg);
			//TODO: temporary solution shall delete
			bKeyHandled = (oldsize == m_events.size());
		}
		else
		{
			newMsg.message = EM_CTRL_UPDATEKEY;
			bKeyHandled = false;
			MsgProc(&newMsg);
		}
	}
#endif
	return bKeyHandled;
}

int CGUIRoot::HandleUserInput()
{
	TouchSessions::GetInstance().TickTouchSessions();

	if (!m_bActive) {
		m_events.clear();
		return 0;
	}

	PERF1("GUI Framemove");

	CheckLoadCursor();

	CGUIPosition pos;
	CGUIBase* temp = NULL;
	int returnvalue = 0;
	bool bKeyHandled = false;
	bool bMouseHandled = false;
	STRUCT_DRAG_AND_DROP *pdrag = &IObjectDrag::DraggingObject;

	if (false && CGlobals::GetApp()->IsTouchInputting())
	{
		DispatchTouchMouseEvent(bMouseHandled);
		bKeyHandled = DispatchKeyboardMsg(bKeyHandled);
	}
	else
	{
		m_events.clear();
		{
			// mouse update
			static int g_ptBeforeLockMouseX, g_ptBeforeLockMouseY;
			int nBufferedMessgeCount = m_pMouse->GetBufferedMessageCount();
			m_pMouse->Update();
			if (!m_pMouse->IsLocked())
			{
#ifdef PARAENGINE_MOBILE
				if (nBufferedMessgeCount > 0)
					UpdateCursorPosition();
#else
				UpdateCursorPosition();
#endif
				m_pMouse->GetDeviceCursorPos(g_ptBeforeLockMouseX, g_ptBeforeLockMouseY);
			}

			
			if (!CGlobals::GetApp()->IsWindowedMode())
			{
				m_pMouse->SetDeviceCursorPos(g_ptBeforeLockMouseX, g_ptBeforeLockMouseY);
			}
		}
		POINT pt = { m_pMouse->m_x, m_pMouse->m_y };
		//the new msg object
		MSG newMsg;
		CEventBinding::InitMsg(&newMsg, GetTickCount(), EM_NONE, pt);

		CGUIContainer* TopCtrl = GetTopLevelControl();

		bKeyHandled = DispatchKeyboardMsg(bKeyHandled);

		CGUIBase* pMouseTarget = m_pMouse->GetCapture();
		bool bHasMouseCapture = false;
		// Fixed 2010.10.27: the mouse target is always the currently captured mouse object. Scrollbar will function correctly. 
		if (pMouseTarget == NULL)
			pMouseTarget = GetUIObject(m_pMouse->m_x, m_pMouse->m_y);
		else
			bHasMouseCapture = true;

		bool bCollapseMouseMove = false;
		//
		// For each queued Direct mouse input, generate a raw mouse message and send to the responsible gui object to process
		//
		for (DWORD i = 0; i < m_pMouse->m_dwElements; i++)
		{
			bMouseHandled = false;
			ZeroMemory(&newMsg, sizeof(MSG));
			newMsg.time = m_pMouse->m_didod[i].dwTimeStamp;
			newMsg.hwnd = CGlobals::GetAppHWND();
			newMsg.message = EM_NONE;
			switch (m_pMouse->m_didod[i].dwOfs)
			{
			case DIMOFS_BUTTON0:
				if (m_pMouse->m_didod[i].dwData & 0x80){
					newMsg.message = EM_MOUSE_LEFTDOWN;
					//update key state according to the event.
					CGUIEvent::KeyStates[EM_MOUSE_LEFTDOWN] = (byte)m_pMouse->m_didod[i].dwData;
					if (bHasMouseCapture)
					{
						// this fixed a bug, when a button GUI may fail to release capture for a number of tricky reasons. 
						// Any left click will automatically release any old captured button
						bHasMouseCapture = false;
						pMouseTarget = GetUIObject(m_pMouse->m_x, m_pMouse->m_y);
					}
				}
				else{
					newMsg.message = EM_MOUSE_LEFTUP;
					//update key state according to the event.
					CGUIEvent::KeyStates[EM_MOUSE_LEFTDOWN] = (byte)m_pMouse->m_didod[i].dwData;

				}
				bCollapseMouseMove = false;
				break;

			case DIMOFS_BUTTON1:
				if (m_pMouse->m_didod[i].dwData & 0x80){
					newMsg.message = EM_MOUSE_RIGHTDOWN;
					//update key state according to the event.
					CGUIEvent::KeyStates[EM_MOUSE_RIGHTDOWN] = (byte)m_pMouse->m_didod[i].dwData;
				}
				else{
					newMsg.message = EM_MOUSE_RIGHTUP;
					//update key state according to the event.
					CGUIEvent::KeyStates[EM_MOUSE_RIGHTDOWN] = (byte)m_pMouse->m_didod[i].dwData;
				}
				bCollapseMouseMove = false;
				break;
			case DIMOFS_BUTTON2:
				if (m_pMouse->m_didod[i].dwData & 0x80){
					newMsg.message = EM_MOUSE_MIDDLEDOWN;
					//update key state according to the event.
					CGUIEvent::KeyStates[EM_MOUSE_MIDDLEDOWN] = (byte)m_pMouse->m_didod[i].dwData;
				}
				else{
					newMsg.message = EM_MOUSE_MIDDLEUP;
					//update key state according to the event.
					CGUIEvent::KeyStates[EM_MOUSE_MIDDLEDOWN] = (byte)m_pMouse->m_didod[i].dwData;
				}
				bCollapseMouseMove = false;
				break;
			case DIMOFS_X:
				newMsg.message = EM_MOUSE_MOVE;
				newMsg.lParam = (int)m_pMouse->m_didod[i].dwData;
				newMsg.wParam = 0;
				// 2008.6.19. LiXizhi. mouse position is updated completely via windows ::GetCursorPosition(), instead of d3d cursor. So we need not update it. 
				//m_pMouse->UpdateX((int)m_pMouse->m_didod[ i ].dwData);
				break;

			case DIMOFS_Y:
				newMsg.message = EM_MOUSE_MOVE;
				newMsg.lParam = 0;
				newMsg.wParam = (int)m_pMouse->m_didod[i].dwData;
				// 2008.6.19. LiXizhi. mouse position is updated completely via windows ::GetCursorPosition(), instead of d3d cursor. So we need not update it. 
				//m_pMouse->UpdateY((int)m_pMouse->m_didod[ i ].dwData);
				break;

			case DIMOFS_Z:
				newMsg.message = EM_MOUSE_WHEEL;
				newMsg.lParam = (int)m_pMouse->m_didod[i].dwData;
				//pMouseEvent->m_mouse.WheelDelta=(int)m_pMouse->m_didod[ i ].dwData;
				break;

			}
			if (newMsg.message == EM_MOUSE_MOVE)
			{
				if (bCollapseMouseMove)
				{
					continue;
				}
				else
				{
					bCollapseMouseMove = true;
				}
			}
			newMsg.pt.x = m_pMouse->m_x;
			newMsg.pt.y = m_pMouse->m_y;
			bool isAnyButtonDown = newMsg.message == EM_MOUSE_LEFTDOWN || newMsg.message == EM_MOUSE_RIGHTDOWN || newMsg.message == EM_MOUSE_MIDDLEDOWN;
		
			if (!CGlobals::GetScene()->IsPickingObject() 
				// we should not let GUI to receive any event when scene is capturing the mouse.
				&& !(IsMouseCaptured() && m_pLastMouseDownObject == CGlobals::GetScene()))
			{
				if (pdrag->pDragging != NULL)
				{
					if (pdrag->m_bIsCandicateOnly)
					{
						if (m_pMouse->IsButtonDown(CDirectMouse::LEFT_BUTTON))
						{
							((CGUIBase*)pdrag->pDragging)->MsgProc(&newMsg);
							bMouseHandled = true;
						}
						else
						{
							pdrag->UnsetDraggingCandidate();
						}
					}
					else
					{
						// if there is an object being dragged, only let the dragging object alone (not including its children) to process the mouse message. 
						if (newMsg.message == EM_MOUSE_MOVE)
						{
							newMsg.message = EM_MOUSE_DRAGOVER;
						}
						if (newMsg.message == EM_MOUSE_LEFTDOWN)
						{
							// cancel the drag operation if a mouse down event is encountered during the course of a dragging operation. 
							// this does not happen, except on some very rare conditions when things go wrong. 
							IObjectDrag::CancelDrag(pdrag);
							// force release capture
							m_pMouse->ReleaseCapture((CGUIBase*)pdrag);
						}
						else
						{
							((CGUIBase*)pdrag->pDragging)->MsgProc(&newMsg);
						}
						bMouseHandled = true;
					}
				}
				//else if (m_pMouse->GetCapture()) 
				//{
				//	// OBSOLETED: DirectMouse no longer uses Capture code. This section of code is never called. 
				//	OUTPUT_LOG("warning: DirectMouse no longer uses Capture code. This section of code should never be called. \n");
				//	bMouseHandled = m_pMouse->GetCapture()->MsgProc(&newMsg);
				//}
				if (!bMouseHandled)
				{
					if (NULL != pMouseTarget)
					{
						// if mouse pointer is over an UI object: pMouseTarget stores the top (leaf) UI object which the mouse pointer falls in.
						if (pMouseTarget != GetUIMouseFocus())
						{
							/** if current mouse targeted object is different from the current top most mouse focus object,
							* we shall set the current mouse targeted object as current mouse focus object.
							*/
							// added by LiXizhi 2007.6.3, this will prevent listbox and other controls to behave wrongly.
							// - Bug corrected: The ListBox freezes whenever a top level control is pops up and closes. 
							// This is due to the CGUIListBox::m_nEvent.m_eState not reset after losing focus. Hence, whenever a control is re-gaining focus, we will reset its key state.
							pMouseTarget->m_event->ResetState();
							SetUIMouseFocus(pMouseTarget);
							/*if (pMouseTarget->m_parent)
							{
							pMouseTarget->m_parent->SetMouseFocus(pMouseTarget);
							}*/
						}

						if (TopCtrl == pMouseTarget)
						{
							// if the top level container is the mouse target, let it alone to process the message. this prevents TopCtrl to send Z-order messages to its parent(usually the GUIRoot).
							// a top level container is the mouse target only if the mouse pointer does not falls into any of its children. 
							// In other word, top level container is full screen regardless of its size. 
							bMouseHandled = pMouseTarget->MsgProc(&newMsg);
						}
						else
						{
							// let the leaf mouse targeted object to handle the raw message first, if leaf does not handle it, the message is sent to its parent for processing, 
							// and so on until either some object processes it or the root or top control object is met. 
							bool bMouseTargetParentIsTopLevelCtrl = false;
							temp = pMouseTarget;
							while (temp && !(bMouseHandled |= temp->MsgProc(&newMsg))
								&& !(bMouseTargetParentIsTopLevelCtrl = (temp == TopCtrl)))
							{
								temp = temp->m_parent;

							}
							if (!bMouseTargetParentIsTopLevelCtrl)
							{
								// bring the mouse target window to front and activate it, if event is mouse down and parent is not a top level control
								if (pMouseTarget->m_event->IsMapTo(newMsg.message, EM_MOUSE_DOWN))
								{
									BringToFront(pMouseTarget);
									// call the WM_activate script. TODO: shall we call the deactivate script for the last window that loses focus. 
									// please note that this function might be called multiple times for the same window. 
									pMouseTarget->MakeActivate(1);
								}
							}
						}
					}
					else
					{
						// if mouse point is not on any GUI object, just let the GUIRoot process the message and reset mouse focus. 
						bMouseHandled = MsgProc(&newMsg);
						SetUIMouseFocus(NULL);
					}
				}

				if (isAnyButtonDown)
				{
					if (bMouseHandled)
						m_pLastMouseDownObject = pMouseTarget;
					else
						m_pLastMouseDownObject = CGlobals::GetScene();
				}
			}

			if (pdrag->pDragging != NULL && !pdrag->m_bIsCandicateOnly) {
				((CGUIBase*)pdrag->pDragging)->SetLocation(newMsg.pt.x + pdrag->nRelativeX, newMsg.pt.y + pdrag->nRelativeY);
			}
			if (TopCtrl != 0)
			{
				// this prevent any mouse messages to be sent to 3D when there is a top level control. 
				bMouseHandled = true;
			}
			else if (!bMouseHandled || m_pLastMouseDownObject == CGlobals::GetScene()) 
			{
				m_events.push_back(newMsg);
			}

			if (newMsg.message == EM_MOUSE_LEFTUP || newMsg.message == EM_MOUSE_RIGHTUP || newMsg.message == EM_MOUSE_MIDDLEUP)
				m_pLastMouseDownObject = NULL;

			if (i == (m_pMouse->m_dwElements - 1)){
				//check the if KeyStates matches the hardware state
				//if not, the lost messages will be sent to the object again.
			}
		}

		//send a framemove message to the mouse target as well as all its parents
		{
			ZeroMemory(&newMsg, sizeof(MSG));
			POINT pt;
			CEventBinding::InitMsg(&newMsg, GetTickCount(), EM_CTRL_FRAMEMOVE, pt);
			newMsg.pt.x = m_pMouse->m_x;
			newMsg.pt.y = m_pMouse->m_y;
			if (pdrag->pDragging != NULL)
			{
				((CGUIBase*)pdrag->pDragging)->MsgProc(&newMsg);
			}
			else if (m_pMouse->GetCapture())
			{
				m_pMouse->GetCapture()->MsgProc(&newMsg);
			}
			else if (pMouseTarget != NULL)
			{
				temp = pMouseTarget;
				while (temp && !temp->MsgProc(&newMsg))
				{
					temp = temp->m_parent;
				}
			}
		}
	}

	m_bKeyboardProcessed = (bKeyHandled || (GetUIKeyFocus() != NULL));
	m_bMouseProcessed = (bMouseHandled || (GetUIMouseFocus() != NULL) || (pdrag->pDragging != NULL));

	//we sort the messages here, so all the message are in ascending order, no matter it is a mouse event or keyboard event.
	std::stable_sort(m_events.begin(), m_events.end(), LessMSGCompare());

	//post delete object
	vector<int>::iterator iterp, iterpend = m_deleteQueue.end();
	for (iterp = m_deleteQueue.begin(); iterp != iterpend; iterp++){
		DestroyGUIElement(GetUIObject(*iterp));
	}
	m_deleteQueue.clear();

#ifdef USE_DIRECTX_RENDERER
	if(!m_bMouseProcessed && m_pMouse!=0)
	{
		int nHotSpotX = -1;
		int nHotSpotY = -1;
		const std::string& sCursorFile = CGlobals::GetScene()->GetCursor(&nHotSpotX, &nHotSpotY);
		if(!sCursorFile.empty())
		{
			m_pMouse->SetCursorFromFile(sCursorFile.c_str(), nHotSpotX, nHotSpotY);
		}
	}
#endif
	return 0;
}

//no need refactoring
CGUIBase* CGUIRoot::GetUIKeyFocus()
{
	CGUIBase* pDest = this;
	while (((CGUIType*)pDest->GetType())->IsContainer() && pDest->m_bIsEnabled&&pDest->m_bIsVisible) {
		CGUIBase* pDestTmp = ((CGUIContainer*)pDest)->GetKeyFocus();
		if (pDestTmp) {
			pDest = pDestTmp;
		}
		else{
			break;
		}
	}
	if (pDest->GetType()->GetTypeValue() == Type_GUIRoot) {
		pDest = NULL;
	}
	return pDest;
}

/**
Each container type object stores a mouse focus object, which can be NULL or one of its children.
The GUIRoot object's GetUIMouseFocus returns the top level(the leaf one) mouse focus object.
*/
CGUIBase* CGUIRoot::GetUIMouseFocus()
{
	CGUIBase* pDest = this;
	while (((CGUIType*)pDest->GetType())->IsContainer())
	{
		if (((CGUIContainer*)pDest)->GetMouseFocus())
		{
			pDest = ((CGUIContainer*)pDest)->GetMouseFocus();
		}
		else
		{
			break;
		}
	}
	if (pDest->GetType()->GetTypeValue() == Type_GUIRoot)
	{
		pDest = NULL;
	}
	return pDest;
}

void CGUIRoot::SetUIKeyFocus(CGUIBase* control)
{
}

/** added by Xizhi 2009.10.12
Set the UI mouse focus to the given control, and unset previous focused controls.
The algorithm is below.

find the closest common parent:
compare input control's parent with all controls that has focus from top to bottom.
*/
void CGUIRoot::SetUIMouseFocus(CGUIBase* control)
{
	// step1: find the closest common parent of the control gaining focus and the control losing focus. 
	CGUIContainer* common_parent = NULL;
	if (control == NULL)
	{
		common_parent = this;
	}
	else
	{
		CGUIContainer* control_parent = control->GetParent();
		while (common_parent == 0 && control_parent != 0)
		{
			CGUIBase* common_parent_candicate = this;
			while (common_parent_candicate != 0 && ((CGUIType*)common_parent_candicate->GetType())->IsContainer())
			{
				if (common_parent_candicate == control_parent)
				{
					// found the common parent
					common_parent = (CGUIContainer*)common_parent_candicate;
					break;
				}
				common_parent_candicate = ((CGUIContainer*)common_parent_candicate)->GetMouseFocus();
			}
			control_parent = control_parent->GetParent();
		}
	}

	// step2: remove focus from all controls in the chain from the common_parent down to the unfocused control. 
	if (common_parent)
	{
		CGUIBase* pDest = common_parent;
		while (((CGUIType*)pDest->GetType())->IsContainer())
		{
			CGUIContainer* pContainer = ((CGUIContainer*)pDest);

			if (pContainer->GetMouseFocus())
			{
				pDest = pContainer->GetMouseFocus();
				if (control != pDest)
				{
					pContainer->SetMouseFocus(NULL);
					// send mouse leave message to pDest
					pDest->OnMouseLeave();
					m_tooltip->DeactivateTip(pDest);
					m_pMouse->ReleaseCapture(pDest);
					pDest->ClearEvent(CGUIEvent::MOUSE);
				}
			}
			else
			{
				break;
			}
		}
	}
	if (control && control != common_parent)
	{
		// set all mouse focus next common_parent is met. 
		CGUIContainer* control_parent = control->GetParent();
		do
		{
			// send mouse enter message to control
			{
				control->OnMouseEnter();
				if (control->GetVisible() && control->GetEnabled())
				{
					m_tooltip->ActivateTip(control);
				}
			}

			control_parent = control->GetParent();
			if (control_parent != 0)
			{
				control_parent->SetMouseFocus(control);
				control = control_parent;
			}
		} while ((control_parent != common_parent) && (control_parent != 0));
	}
}

//no need refactoring
void CGUIRoot::UseDefaultMouseCursor(bool bUseDefaultMouseCursor)
{
#ifdef USE_DIRECTX_RENDERER
	if(m_pMouse)
	{
		//if(m_pMouse->m_bUseDefaultCursor != bUseDefaultMouseCursor)
		//{
		//	m_pMouse->m_bUseDefaultCursor = bUseDefaultMouseCursor;
		//	if(bUseDefaultMouseCursor)
		//	{
		//		//HCURSOR hc=LoadCursor(NULL,IDC_ARROW);
		//		//::SetCursor(hc);
		//		const string& sCursor = GetCursor();
		//		if(!sCursor.empty())
		//		{
		//			m_pMouse->SetCursorFromFile(":IDR_DEFAULT_CURSOR",-1, -1);
		//		}
		//	}
		//	else{
		//		m_pMouse->SetCursorFromFile(NULL);
		//	}
		//}
		//else
		//{
		//	if(bUseDefaultMouseCursor)
		//	{
		//		const string& sCursor = GetCursor();
		//		if(!sCursor.empty())
		//		{
		//			m_pMouse->SetCursorFromFile(":IDR_DEFAULT_CURSOR", -1, -1);
		//		}
		//	}
		//}
		if(bUseDefaultMouseCursor)
		{
			int nHotSpotX = -1;
			int nHotSpotY = -1;
			const std::string& sCursorFile = GetCursor(&nHotSpotX, &nHotSpotY);
			if(!sCursorFile.empty())
			{
				m_pMouse->SetCursorFromFile(sCursorFile.c_str(), nHotSpotX, nHotSpotY);
			}
			else
			{
				// prevent waiting cursor to show up
				m_pMouse->SetCursorFromFile(":IDR_DEFAULT_CURSOR", -1, -1);
				/*HCURSOR hc = LoadCursor(NULL, IDC_ARROW);
				::SetCursor(hc);*/
			}
		}
	}
#endif
}


bool CGUIRoot::MsgProc(MSG *event)
{
	bool bHandled = false;
	bHandled = true;
	if (m_event->IsMapTo(event->message, EM_MOUSE_DRAGBEGIN)) {
		bHandled = false;
	}
	else if (m_event->IsMapTo(event->message, EM_MOUSE_DRAGEND)) {
		bHandled = false;
	}
	else if (m_event->IsMapTo(event->message, EM_MOUSE_DRAGOVER)) {
		bHandled = false;
	}
	else if (m_event->IsMapTo(event->message, EM_CTRL_UPDATEKEY)) {
		bHandled = false;
	}
	else if (m_event->IsMapTo(event->message, EM_CTRL_FOCUSIN)) {
		//let the default msgproc to handle the focusin event
		//but we still tell the caller that the message is not processed
		CGUIBase::MsgProc(event);
	}
	if (!bHandled) {
		return CGUIBase::MsgProc(event);
	}
	else
		return false;
}

static CGUIRoot* g_root = NULL;

//shall change to singleton
CGUIRoot* CGUIRoot::GetInstance()
{
	if (g_root != 0)
		return g_root;
	else
		return NULL;
}

CGUIRoot* CGUIRoot::CreateInstance()
{
	if (g_root == 0)
	{
		g_root = new CGUIRoot();
	}
	return g_root;
}

void CGUIRoot::DeleteInstance(CGUIRoot* pThis)
{
	if (pThis != 0)
	{
		if (g_root == pThis)
		{
			g_root = NULL;
		}
		delete pThis;
	}
	else
	{
		SAFE_DELETE(g_root);
	}
}

/** Any better ways to do it?
Added LXZ: 2006.1.1 */
bool CGUIRoot::OnClick(int MouseState, int X, int Y)
{
	//bool res = CGUIBase::OnClick( MouseState, X, Y);
	//// Add to the mouse event pool
	//m_MouseEvents.push_back(MouseEvent(MouseState, X, Y));
	//return res;
	return true;
}

//no need refactoring
LRESULT CGUIRoot::MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool &bNoFurtherProcess)
{
	LRESULT result = 0;
#if defined(USE_DIRECTX_RENDERER) || 0
	MSG newMsg;
	if (uMsg<=WM_MOUSELAST&&uMsg>=WM_MOUSEFIRST) 
	{
		// only process virtual mouse event when not touch inputting, since we will translate to mouse event by ourselves. 
		if (!CGlobals::GetApp()->IsTouchInputting())
		{
			// catches all mouse events
			if (m_pMouse && m_pMouse->IsUseWindowsMessage()) {
				if (GetMouseInClient() || m_pLastMouseDownObject == CGlobals::GetScene()) {
					newMsg.hwnd = hWnd;
					newMsg.lParam = lParam;
					newMsg.wParam = wParam;
					newMsg.time = GetTickCount();
					newMsg.message = uMsg;
					m_pMouse->PushMouseEvent(newMsg);
					bNoFurtherProcess = true;
					//result=TRUE;
				}
			}
		}
	}
	else
	{
		switch( uMsg )
		{
		case WM_SIZE:
		{
			// inform OnSize().
			OnSize();
			break;
		}
		case WM_SETCURSOR:
			// Turn off Windows cursor in fullscreen mode
			if(!GetUseSystemCursor())
			{
				if(m_pMouse && m_pMouse->m_bShowCursor)
					m_pMouse->ForceShowCursor(true);
				bNoFurtherProcess=true;// prevent Windows from setting cursor to window class cursor
				result=TRUE;
			}
			break;
		}	
	}
	if(bNoFurtherProcess)
	{
		return result;
	}

	if(m_pKeyboard)
	{
		if(m_pKeyboard->IsUseWindowsMessage())
		{
			//if (uMsg == WM_SYSCOMMAND && wParam == SC_KEYMENU)
			//{
			//	// translate to alt key up;
			//	uMsg = WM_KEYUP;
			//	wParam = VK_LMENU;
			//}
			if (uMsg == WM_SYSKEYDOWN && (wParam == VK_MENU || wParam == VK_LMENU))
			{
				// translate to alt key up;
				uMsg = WM_KEYDOWN;
				wParam = VK_LMENU;
			}
			else if (uMsg == WM_SYSKEYUP && (wParam == VK_MENU || wParam == VK_LMENU))
			{
				// translate to alt key up;
				uMsg = WM_KEYUP;
				wParam = VK_LMENU;
			}
			// only process key
			if (uMsg == WM_KEYDOWN || uMsg == WM_KEYUP)
			{
				newMsg.hwnd=hWnd;
				newMsg.lParam=lParam;
				newMsg.wParam=wParam;
				newMsg.time=GetTickCount();
				newMsg.message=uMsg;
				m_pKeyboard->PushKeyEvent(newMsg);
				//bNoFurtherProcess=true;
				//result=TRUE;
			}
		}
		else
		{
			if(!s_bIMEKeyBoardUpdated)
			{
				s_bIMEKeyBoardUpdated = true;
				result = TRUE;
				m_pKeyboard->Update();
			}
		}
	}
#endif
	return result;
}


bool ParaEngine::CGUIRoot::GetMouseInClient()
{
	return m_bMouseInClient;
}

bool ParaEngine::CGUIRoot::CheckLoadCursor()
{
#ifdef USE_DIRECTX_RENDERER
	if (GetMouseInClient() && m_pMouse)
	{
		if (m_pMouse->GetCursorName().empty())
		{
			UseDefaultMouseCursor(true);
			return !m_pMouse->GetCursorName().empty();
		}
	}
#endif
	return true;
}

void CGUIRoot::SetMouseInClient(bool bMouseInClient)
{
#ifdef PARAENGINE_MOBILE
	m_bMouseInClient = bMouseInClient;
#else
	if (m_bMouseInClient!=bMouseInClient) 
	{
		if (bMouseInClient) {
			UseDefaultMouseCursor(false);
		}else{
			UseDefaultMouseCursor(true);
		}
		m_bMouseInClient=bMouseInClient;
	}
	else
	{
		if(!bMouseInClient)
			UseDefaultMouseCursor(true);
	}
#endif
}
void CGUIRoot::SetRenderImageCursor(bool bEnable)
{
	m_bRenderImageCursor = bEnable;
}

HRESULT CGUIRoot::OneTimeGUIInit()
{
	CGUIEvent::StaticInit();
	if (m_pKeyboard == 0)
		m_pKeyboard = new CDirectKeyboard(CGlobals::GetAppHWND());
	if (m_pMouse == 0)
		m_pMouse = new CDirectMouse(CGlobals::GetAppHWND());
#if defined(USE_DIRECTX_RENDERER)
	CGUIIME::OnFocusOut();
#endif

	if (CGlobals::GetAssetManager()->GetFont("sys") == NULL)
	{
		// if sys font is not specified, create it
		CGlobals::GetAssetManager()->LoadGDIFont("sys", "System", 12);
	}

	UpdateCursorPosition();

	//UseDefaultMouseCursor(false);
	Initialize();
	//using namespace ParaInfoCenter;
	//CICConfigManager::test();
	return S_OK;
}

CGUIBase* CGUIRoot::GetDefaultObject(const char *strType)
{
	CObjectManager *pOm = &CSingleton<CObjectManager>::Instance();
	CGUIBase* pObj = NULL;
	if (strcmp(strType, "button") == 0)
	{
		if (!pOm->IsExist("default_CGUIButton")) {
			CGUIButton::StaticInit();
		}
		pObj = (CGUIBase*)pOm->GetObject("default_CGUIButton");
	}
	else if (strcmp(strType, "editbox") == 0)
	{
		if (!pOm->IsExist("default_CGUIEditBox")) {
			CGUIEditBox::StaticInit();
		}
		pObj = (CGUIBase*)pOm->GetObject("default_CGUIEditBox");
	}
	else if (strcmp(strType, "imeeditbox") == 0)
	{
		if (!pOm->IsExist("default_CGUIIMEEditBox")) {
			CGUIIMEEditBox::StaticInit();
		}
		pObj = (CGUIBase*)pOm->GetObject("default_CGUIIMEEditBox");
	}
	else if (strcmp(strType, "scrollbar") == 0)
	{
		if (!pOm->IsExist("default_CGUIScrollbar")) {
			CGUIScrollBar::StaticInit();
		}
		pObj = (CGUIBase*)pOm->GetObject("default_CGUIScrollbar");
	}
	else if (strcmp(strType, "container") == 0)
	{
		if (!pOm->IsExist("default_CGUIContainer")) {
			CGUIContainer::StaticInit();
		}
		pObj = (CGUIBase*)pOm->GetObject("default_CGUIContainer");
	}
	else if (strcmp(strType, "text") == 0)
	{
		if (!pOm->IsExist("default_CGUIText")) {
			CGUIText::StaticInit();
		}
		pObj = (CGUIBase*)pOm->GetObject("default_CGUIText");
	}
	else if (strcmp(strType, "listbox") == 0)
	{
		if (!pOm->IsExist("default_CGUIListBox")) {
			CGUIListBox::StaticInit();
		}
		pObj = (CGUIBase*)pOm->GetObject("default_CGUIListBox");
	}
	else if (strcmp(strType, "slider") == 0)
	{
		if (!pOm->IsExist("default_CGUISlider")) {
			CGUISlider::StaticInit();
		}
		pObj = (CGUIBase*)pOm->GetObject("default_CGUISlider");
	}
	else if (strcmp(strType, "tooltip") == 0)
	{
		pObj = m_tooltip;
	}
	else{
		OUTPUT_LOG("error: nType %s of UIObject, is not supported.\n", strType);
	}
	return pObj;
}

void ParaEngine::CGUIRoot::SetUseSystemCursor(bool bUseSystem)
{
	m_bUseSystemCursor = bUseSystem;
#ifdef USE_DIRECTX_RENDERER
	if(m_bUseSystemCursor)
	{
		UseDefaultMouseCursor(true);
	}
	else
	{
		UseDefaultMouseCursor(false);
		if(m_pMouse->m_bShowCursor)
			m_pMouse->ForceShowCursor(true);
	}
#endif
}

bool ParaEngine::CGUIRoot::GetUseSystemCursor()
{
	return m_bUseSystemCursor;
}

void ParaEngine::CGUIRoot::AddToNameMap(const string& name, CGUIBase* pObj)
{
	m_namemap[name] = pObj;
}

void ParaEngine::CGUIRoot::AddToIDMap(int nID, CGUIBase* pObj)
{
	m_idmap[nID] = pObj;
}


bool CGUIRoot::UpdateViewport(int nLeft, int nTop, int nWidth, int nHeight, bool bForceUpdate)
{
	GUIState* pGUIState = &m_stateGUI;

	m_fViewportLeft = (float)nLeft;
	m_fViewportTop = (float)nTop;
	m_fViewportWidth = (float)nWidth;
	m_fViewportHeight = (float)nHeight;

	float fScaleX = 1.f;
	float fScaleY = 1.f;
	GetUIScale(&fScaleX, &fScaleY);
	if (fScaleX != 1.f)
	{
		nWidth = (int)((float)(nWidth) / fScaleX);
	}

	if (fScaleY != 1.f)
	{
		nHeight = (int)((float)(nHeight) / fScaleY);
	}
	
	bool bSizeChanged = (pGUIState->nBkbufWidth != nWidth || pGUIState->nBkbufHeight != nHeight);
	if (bForceUpdate || bSizeChanged)
	{
		pGUIState->nBkbufWidth = nWidth;
		pGUIState->nBkbufHeight = nHeight;

		// update root size. 
		CGUIPosition posDest(0, 0, m_stateGUI.nBkbufWidth, m_stateGUI.nBkbufHeight);
		posDest.SetPositionType(CGUIPosition::relative_to_screen);
		posDest.Relative.To2D.alignType = CGUIPosition::upper_left;
		m_position = posDest;
		m_objResource->SetDrawingRects(&m_position.rect, 0);
		m_objResource->SetDrawingRects(&m_position.rect, 1);

		UpdateRects();
		OnSize();
	}
	return bSizeChanged;
}

void ParaEngine::CGUIRoot::SetUIScale(float fScalingX, float fScalingY, bool bEnsureMinimumScreenSize, bool bNotifySizeChange)
{
	if (fScalingX > 0.f)
		m_fUIScalingX = fScalingX;
	if (fScalingY > 0.f)
		m_fUIScalingY = fScalingY;

	if (bNotifySizeChange)
	{
		if (bEnsureMinimumScreenSize)
		{
			SetMinimumScreenSize(-1, -1, true);
		}
		else
		{
			CViewport* pViewport = CGlobals::GetViewportManager()->CreateGetViewPort(0);
			UpdateViewport(pViewport->GetLeft(), pViewport->GetTop(), pViewport->GetWidth(), pViewport->GetHeight(), true);
		}
	}
}

void ParaEngine::CGUIRoot::GetUIScale(float* pfScalingX, float* pfScalingY)
{
	if (pfScalingX)
		*pfScalingX = GetUIScalingX();

	if (pfScalingY)
		*pfScalingY = GetUIScalingY();
}

void ParaEngine::CGUIRoot::UpdateCursorPosition()
{
	int inout_x, inout_y;
	CGlobals::GetApp()->GetCursorPosition(&inout_x, &inout_y);
	TranslateMousePos(inout_x, inout_y);
	m_pMouse->m_x = inout_x;
	m_pMouse->m_y = inout_y;
}

void ParaEngine::CGUIRoot::SetMinimumScreenSize(int nWidth, int nHeight, bool bAutoUIScaling)
{
	if (nWidth > 0)
		m_fMinScreenWidth = (float)nWidth;
	if (nHeight > 0)
		m_fMinScreenHeight = (float)nHeight;

	if (bAutoUIScaling)
	{
		CViewport* pViewport = CGlobals::GetViewportManager()->CreateGetViewPort(0);
		float fWidth = (float)pViewport->GetWidth();
		float fHeight = (float)pViewport->GetHeight();

		float fScaleX = fWidth / m_fMinScreenWidth;
		float fScaleY = fHeight / m_fMinScreenHeight;
		if (fScaleX < 1.f || fScaleY < 1.f)
		{
			// take the larger one
			if (fScaleX < fScaleY)
				fScaleY = fScaleX;
			else
				fScaleX = fScaleY;
			SetUIScale(fScaleX, fScaleY, false);
		}
		else
		{
			SetUIScale(-1.f, -1.f, false);
		}
	}
}

bool ParaEngine::CGUIRoot::HasIMEFocus()
{
	ParaEngine::Lock lock(m_mutex);
	return m_bHasIMEFocus;
}

void ParaEngine::CGUIRoot::SetHasIMEFocus(bool bHasFocus)
{
	ParaEngine::Lock lock(m_mutex);
	m_bHasIMEFocus = bHasFocus;
}


bool ParaEngine::CGUIRoot::GetEnableIME()
{
#ifdef USE_DIRECTX_RENDERER
	return CGUIIME::IsEnableImeSystem();
#else
	return false;
#endif
}

void ParaEngine::CGUIRoot::SetEnableIME(bool bEnableIME)
{
#ifdef USE_DIRECTX_RENDERER
	CGUIIME::EnableImeSystem(bEnableIME);
#endif
}

bool ParaEngine::CGUIRoot::IsCursorClipped()
{
	return m_bIsCursorClipped;
}

void ParaEngine::CGUIRoot::EnableClipCursor(bool bEnable)
{
#ifdef USE_DIRECTX_RENDERER
	if(CGlobals::GetSettings().IsWindowedMode())
	{
		if(bEnable !=  m_bIsCursorClipped)
		{
			if(bEnable)
			{
				// Record the area in which the cursor can move. 
				if(GetClipCursor(&m_rcOldClipRect))
				{
					RECT rcClip, rcClient;
					HWND hWnd = CGlobals::GetAppHWND();
					// Get the dimensions of the application's window. 
					// GetWindowRect(hWnd, &rcClip);

					POINT pt;
					pt.x=0;
					pt.y=0;
					ClientToScreen(hWnd, &pt);

					GetClientRect(hWnd, &rcClient);
					rcClip.left = pt.x;
					rcClip.top  = pt.y;
					rcClip.right = rcClip.left + rcClient.right;
					rcClip.bottom = rcClip.top + rcClient.bottom;

					{
						// Confine the cursor to the application's window. 
						if(ClipCursor(&rcClip))
						{
							m_bIsCursorClipped = true;
						}
					}
				}
			}
			else
			{
				// Restore the cursor to its previous area. 
				ClipCursor(&m_rcOldClipRect); 
				m_bIsCursorClipped = false;
			}
		}
	}
#endif
}

void ParaEngine::CGUIRoot::GetBackBufferSize(float* pWidth, float* pHeight)
{
#ifdef USE_DIRECTX_RENDERER
	if(pWidth)
		*pWidth = (float)(CGlobals::GetDirectXEngine().GetBackBufferWidth());
	if(pHeight)
		*pHeight = (float)(CGlobals::GetDirectXEngine().GetBackBufferHeight());
#else
	if (pWidth)
		*pWidth = (float)GetWidth();
	if (pHeight)
		*pHeight = (float)GetHeight();
#endif
}

float ParaEngine::CGUIRoot::GetViewportLeft() const
{
	return m_fViewportLeft;
}

float ParaEngine::CGUIRoot::GetViewportTop() const
{
	return m_fViewportTop;
}

void ParaEngine::CGUIRoot::SetCaptureMouse(bool bCapture)
{
	m_bMouseCaptured = bCapture;
	if (bCapture)
		CGlobals::GetApp()->PostWinThreadMessage(PE_WM_SETCAPTURE, 0, 0);
	else
		CGlobals::GetApp()->PostWinThreadMessage(PE_WM_RELEASECAPTURE, 0, 0);
}

bool ParaEngine::CGUIRoot::IsMouseCaptured()
{
	return m_bMouseCaptured;
}

bool ParaEngine::CGUIRoot::PushEvent(const MSG& msg)
{
	if (msg.message == EM_MOUSE_WHEEL)
	{
		// merge all mouse wheel message. 
		for (auto& event : m_events)
		{
			if (event.message == EM_MOUSE_WHEEL)
			{
				event.lParam += msg.lParam;
				return true;
			}
		}
	}
	m_events.push_back(msg);
	return true;
}


void ParaEngine::CGUIRoot::TranslateTouchEvent(const TouchEvent &touch)
{
	if (TouchSessions::GetInstance().InterpreteTouchGestures(&touch))
		return;

	// Note: we will only translate to click event when there is only a single touch event session to avoid multiple touches on GUI control. 
	// Multi-touch is only available via handling the "ontouch" event. 
	if (TouchSessions::GetInstance().GetSessionCount() == 1)
	{
		TouchEventSession* pTouchSession = TouchSessions::GetInstance()[0];
		if (!pTouchSession)
			return;
		int mouse_x = (int)touch.m_x;
		int mouse_y = (int)touch.m_y;
		int ui_mouse_x = pTouchSession->GetCurrentEvent().GetClientPosX();
		int ui_mouse_y = pTouchSession->GetCurrentEvent().GetClientPosY();
		static int s_lastMouseWheelPosY = 0;

		if (touch.m_nTouchType == TouchEvent::TouchEvent_POINTER_UP)
		{
			// OUTPUT_LOG("touch up event: %d %d\n", touch.m_nTouchType, pTouchSession->GetTag());

			if (pTouchSession->GetTag() == 1)
			{
				CGUIRoot::GetInstance()->GetMouse()->PushMouseEvent(WM_LBUTTONUP, 0, MAKELPARAM(mouse_x, mouse_y));
			}
			else if (pTouchSession->GetTag() == 2)
			{
				CGUIRoot::GetInstance()->GetMouse()->PushMouseEvent(WM_RBUTTONUP, 0, MAKELPARAM(mouse_x, mouse_y));
			}
			else if (pTouchSession->GetTag() == -1)
			{
				AutoLocateTouchClick(ui_mouse_x, ui_mouse_y, mouse_x, mouse_y);

				if (pTouchSession->GetDuration() < 300)
				{
					// OUTPUT_LOG("touch translate to click\n");
					// short tap for left mouse click
					CGUIRoot::GetInstance()->GetMouse()->PushMouseEvent(WM_LBUTTONDOWN, 0, MAKELPARAM(mouse_x, mouse_y));
					CGUIRoot::GetInstance()->GetMouse()->PushMouseEvent(WM_LBUTTONUP, 0, MAKELPARAM(mouse_x, mouse_y));
				}
				else
				{
					// long tap for right mouse click
					CGUIRoot::GetInstance()->GetMouse()->PushMouseEvent(WM_RBUTTONDOWN, 0, MAKELPARAM(mouse_x, mouse_y));
					CGUIRoot::GetInstance()->GetMouse()->PushMouseEvent(WM_RBUTTONUP, 0, MAKELPARAM(mouse_x, mouse_y));
				}
			}
		}
		else if (touch.m_nTouchType == TouchEvent::TouchEvent_POINTER_DOWN)
		{
			s_lastMouseWheelPosY = ui_mouse_y;
			pTouchSession->SetTag(-1);
		}
		else if (touch.m_nTouchType == TouchEvent::TouchEvent_POINTER_UPDATE)
		{
			if (pTouchSession->GetTag() == -1)
			{
				// OUTPUT_LOG("touch update: %d %d (drag:%d)\n", ui_mouse_x, ui_mouse_y, pTouchSession->GetMaxDragDistance());
				if (pTouchSession->GetMaxDragDistance() >= pTouchSession->GetFingerSize())
				{
					// this is not a click 
					CGUIBase* pUIObj = GetUIObject(ui_mouse_x, ui_mouse_y);
					if (pUIObj && pUIObj->IsScrollableOrHasMouseWheelRecursive())
					{
						// OUTPUT_LOG("touch wheel: %d %d (drag:%d)\n", ui_mouse_x, ui_mouse_y, pTouchSession->GetMaxDragDistance());
						// simulate a mouse wheel event(disable mouse down/up/move), if the UI control below is scrollable. 
						pTouchSession->SetTag(3);
					}
					else
					{
						// due to Multiple viewport, we need to use the untranslated event, instead of following translated ones. 
						//mouse_x = (int)pTouchSession->GetStartEvent().m_x;
						//mouse_y = (int)pTouchSession->GetStartEvent().m_y;

						// press hold and drag for right button drag, drag directly for left button drag. 
						if (pTouchSession->GetDuration() < 500)
						{
							CGUIRoot::GetInstance()->GetMouse()->PushMouseEvent(WM_LBUTTONDOWN, 0, MAKELPARAM(mouse_x, mouse_y));
							pTouchSession->SetTag(1);
						}
						else
						{
							CGUIRoot::GetInstance()->GetMouse()->PushMouseEvent(WM_RBUTTONDOWN, 0, MAKELPARAM(mouse_x, mouse_y));
							pTouchSession->SetTag(2);
						}
					}
				}
			}
			// how many pixels to be regarded as one mouse wheel step. must be divisible to 120. 
			const float fMouseWheelDragStep = 20.f;
			if (pTouchSession->GetMaxDragDistance() >= fMouseWheelDragStep)
			{
				float fOffsetY = (float)(ui_mouse_y - s_lastMouseWheelPosY);
				if (Math::Abs(fOffsetY) >= fMouseWheelDragStep)
				{
					int ui_touch_x = touch.GetClientPosX();
					int ui_touch_y = touch.GetClientPosY();
					TranslateMousePos(ui_touch_x, ui_touch_y);
					CGUIBase* pUIObj = GetUIObject(ui_touch_x, ui_touch_y);
					if (pUIObj && pUIObj->IsScrollableOrHasMouseWheelRecursive())
					{
						// vertical drag move is always mapped to mouse wheel anyway. 
						int nScrollY = (int)(fOffsetY * WHEEL_DELTA / fMouseWheelDragStep);
						CGUIRoot::GetInstance()->GetMouse()->PushMouseEvent(WM_MOUSEWHEEL, MAKEWPARAM(0, nScrollY), 0);
						s_lastMouseWheelPosY = ui_mouse_y;
						// OUTPUT_LOG("EM_MOUSE_WHEEL %d: delta: %d time:%d \n", pTouchSession->GetTouchId(), (int)(msg.lParam), touch.GetTime());
					}
				}
			}
			if (pTouchSession->GetTag() != 3)
			{
				if (pTouchSession->GetTag() == -1)
				{
					// if mouse button down state is not determined, will reset mouse to make the mouse delta to 0 in the next frame. 
					CGUIRoot::GetInstance()->GetMouse()->ResetLastMouseState();
				}
				CGUIRoot::GetInstance()->GetMouse()->PushMouseEvent(WM_MOUSEMOVE, 0, MAKELPARAM(mouse_x, mouse_y));
			}
		}
		// OUTPUT_LOG("touch session: %d  tag: %d  touch type:%d\n", pTouchSession->GetTouchId(), pTouchSession->GetTag(), touch.m_nTouchType);
	}
}

bool ParaEngine::CGUIRoot::AutoLocateTouchClick(int ui_mouse_x, int ui_mouse_y, int &mouse_x, int &mouse_y)
{
	int nFingerSize = GetFingerSizePixels();
	int nStepSize = GetFingerStepSizePixels(); 
	int nLastRadius = 99999;
	int mouse_new_x = -999;
	int mouse_new_y = -999;
	CGUIBase* pLastUIObj = NULL;

	const int nFrom = (int)(nFingerSize / nStepSize /2);
	for (int i = -nFrom; i <= nFrom; ++i)
	{
		for (int j = -nFrom; j <= nFrom; ++j)
		{
			int nX = i*nStepSize + ui_mouse_x;
			int nY = j*nStepSize + ui_mouse_y;
			CGUIBase* pUIObj = GetUIObject(nX, nY);
			if (pUIObj && !pUIObj->IsScrollableOrHasMouseWheelRecursive() && pUIObj->HasClickEvent())
			{
				int nRadius = i*i + j*j;
				if (nRadius < nLastRadius 
					// child object is always selected over parent object
					|| (pLastUIObj && pLastUIObj->IsAncestorOf(pUIObj)))
				{
					// skip UI that is already big enough
					if (pUIObj->GetWidth() < nFingerSize || pUIObj->GetHeight() < nFingerSize)
					{
						nLastRadius = nRadius;
						mouse_new_x = nX;
						mouse_new_y = nY;
						pLastUIObj = pUIObj;
					}
				}
			}
		}
	}
	if (mouse_new_x > -999)
	{
		// OUTPUT_LOG("touch click simulate offset %d(%d), %d(%d)\n", mouse_x, mouse_new_x - ui_mouse_x, mouse_y, mouse_new_y - ui_mouse_y);
		mouse_x += (int)((mouse_new_x - ui_mouse_x)*m_fUIScalingX);
		mouse_y += (int)((mouse_new_y - ui_mouse_y)*m_fUIScalingY);
		return true;
	}
	else
	{
		// OUTPUT_LOG("no touch click simulate offset\n");
	}
	return false;
}

// @note: call this function in main scripting(render) thread
bool ParaEngine::CGUIRoot::handleTouchEvent(const TouchEvent& touch_)
{
	TouchEvent touch = touch_;
	TranslateMousePointInTouchEvent(touch);

	TouchSessions::GetInstance().AddToTouchSession(touch);

	// fire global event
	CGlobals::GetEventsCenter()->FireEvent(touch);

	if (touch.m_nTouchType == TouchEvent::TouchEvent_POINTER_DOWN)
	{
		CGUIBase* pMouseTarget = GetUIObject(touch.GetClientPosX(), touch.GetClientPosY());
		if (pMouseTarget)
		{
			m_touch_id_to_ui_obj[touch.GetTouchId()] = pMouseTarget->GetID();
		}
	}

	bool bTouchEventHandled = false;
	// dispatch the touch events
	TouchEventSession* touchSession = TouchSessions::GetInstance().GetTouchSession(touch.GetTouchId());
	if (touchSession)
	{
		auto iter = m_touch_id_to_ui_obj.find(touch.GetTouchId());
		if (iter != m_touch_id_to_ui_obj.end())
		{
			CGUIBase* pUIObj = GetUIObject(iter->second);
			if (pUIObj)
			{
				if (pUIObj->OnTouch(touch))
					bTouchEventHandled = true;
				if (touch.m_nTouchType == TouchEvent::TouchEvent_POINTER_UP)
				{
					m_touch_id_to_ui_obj.erase(iter);
				}
			}
			else
			{
				m_touch_id_to_ui_obj.erase(iter);
			}
		}
	}

	if (!bTouchEventHandled)
	{
		// only translate event to mouse input if ontouch event is not handled by any GUI object. 
		// due to multiple viewport issue, we need to use the untranslated touch_, instead of touch
		TranslateTouchEvent(touch_);
	}
	return true;
}

bool ParaEngine::CGUIRoot::handleNonClientTest(const MouseEvent& mouseEvent)
{
	bool bIsInClient = (mouseEvent.m_nEventType == 1);
	SetMouseInClient(bIsInClient);
	bool bIsNonClient = false;
	if (bIsInClient)
	{
		CGUIBase* pMouseTarget = GetUIObject(m_pMouse->m_x, m_pMouse->m_y);
		if (pMouseTarget && pMouseTarget->IsNonClientTestEnabled())
		{
			bIsNonClient = true;
		}
	}
	SetIsNonClient(bIsNonClient);
	return bIsInClient;
}

CGUIBase* ParaEngine::CGUIRoot::GetIMEFocus() const
{
	return m_IMEFocus;
}

void ParaEngine::CGUIRoot::SetIMEFocus(CGUIBase* val)
{
	m_IMEFocus = val;
}

void ParaEngine::CGUIRoot::TranslateMousePos(int &inout_x, int &inout_y)
{
	CViewport* pViewport = CGlobals::GetViewportManager()->GetViewportByPoint(inout_x, inout_y);
	int nLeft = (int)m_fViewportLeft;
	int nTop = (int)m_fViewportTop;
	if (pViewport)
	{
		nLeft = pViewport->GetLeft();
		nTop = pViewport->GetTop();
	}
	if (m_fViewportLeft != 0)
		inout_x -= nLeft;
	if (m_fViewportTop != 0)
		inout_y -= nTop;
	if (m_fUIScalingX != 1.f)
		inout_x = (int)((float)inout_x / m_fUIScalingX);
	if (m_fUIScalingY != 1.f)
		inout_y = (int)((float)inout_y / m_fUIScalingY);
}

void ParaEngine::CGUIRoot::TranslateMousePointInTouchEvent(TouchEvent &touch)
{
	int mouse_x = (int)touch.m_x;
	int mouse_y = (int)touch.m_y;
	TranslateMousePos(mouse_x, mouse_y);
	touch.m_x = (float)mouse_x;
	touch.m_y = (float)mouse_y;
}

bool ParaEngine::CGUIRoot::handleAccelerometerEvent(const AccelerometerEvent& accelerator)
{
	CGlobals::GetEventsCenter()->FireEvent(accelerator);
	return false;
}

CPaintEngine * ParaEngine::CGUIRoot::paintEngine() const
{
	if (engine)
		return engine;

	CPaintEngine *engine_ = CPaintEngineGPU::GetInstance();
	if (engine_ && engine_->isActive() && engine_->paintDevice() != this) {
		OUTPUT_LOG("warning: multiple active GPU paint engine. normally there should only be one active one\n");
		engine_ = new CPaintEngineGPU();
		return engine_;
	}
	return engine_;
}

int ParaEngine::CGUIRoot::metric(PaintDeviceMetric metric) const
{
	if (metric == CPaintDevice::PdmWidth)
		return (int)m_fViewportWidth;
	else if (metric == CPaintDevice::PdmHeight)
		return (int)m_fViewportHeight;
	else
		return CPaintDevice::metric(metric);
}

float ParaEngine::CGUIRoot::GetUIScalingX() const
{
	return m_fUIScalingX;
}

float ParaEngine::CGUIRoot::GetUIScalingY() const
{
	return m_fUIScalingY;
}

CPainter* ParaEngine::CGUIRoot::GetPainter()
{
	return m_pPainter;
}

bool ParaEngine::CGUIRoot::handleGesturePinch(CTouchGesturePinch& pinch_gesture)
{
	int nDeltaPixels = pinch_gesture.GetDeltaDistance();
	if (Math::Abs(nDeltaPixels) > 20)
	{
		// zoom in / out one step every 20 pixels
		pinch_gesture.ResetLastDistance();
		int nScrollY = (int)(nDeltaPixels * WHEEL_DELTA / 20);
		CGUIRoot::GetInstance()->GetMouse()->PushMouseEvent(WM_MOUSEWHEEL, MAKEWPARAM(0, nScrollY), 0);
		// OUTPUT_LOG("handleGesturePinch %d\n", nScrollY);
		return true;
	}
	return false;
}

CGUIBase* ParaEngine::CGUIRoot::GetActiveWindow() const
{
	return m_pActiveWindow;
}

void ParaEngine::CGUIRoot::SetActiveWindow(CGUIBase* val)
{
	m_pActiveWindow = val;
}

int ParaEngine::CGUIRoot::InstallFields(CAttributeClass* pClass, bool bOverride)
{
	// install parent fields if there are any. Please replace __super with your parent class name.
	CGUIContainer::InstallFields(pClass, bOverride);
	PE_ASSERT(pClass != NULL);
	pClass->AddField("UIScale", FieldType_Vector2, (void*)SetUIScale_s, (void*)GetUIScale_s, NULL, NULL, bOverride);
	pClass->AddField("MousePosition", FieldType_Vector2, (void*)SetMousePosition_s, (void*)GetMousePosition_s, NULL, NULL, bOverride);
	pClass->AddField("BackBufferSize", FieldType_Vector2, NULL, (void*)GetBackBufferSize_s, NULL, NULL, bOverride);
	pClass->AddField("HasIMEFocus", FieldType_Bool, (void*)SetHasIMEFocus_s, (void*)GetHasIMEFocus_s, NULL, NULL, bOverride);
	pClass->AddField("EnableIME", FieldType_Bool, (void*)SetEnableIME_s, (void*)GetEnableIME_s, NULL, NULL, bOverride);
	pClass->AddField("UseSystemCursor", FieldType_Bool, (void*)SetUseSystemCursor_s, (void*)GetUseSystemCursor_s, NULL, NULL, bOverride);
	pClass->AddField("CaptureMouse", FieldType_Bool, (void*)SetCaptureMouse_s, (void*)IsMouseCaptured_s, NULL, NULL, bOverride);
	pClass->AddField("IsNonClient", FieldType_Bool, (void*)SetIsNonClient_s, (void*)IsNonClient_s, NULL, NULL, bOverride);
	pClass->AddField("FingerSizePixels", FieldType_Int, (void*)SetFingerSizePixels_s, (void*)GetFingerSizePixels_s, NULL, NULL, bOverride);
	pClass->AddField("FingerStepSizePixels", FieldType_Int, (void*)SetFingerStepSizePixels_s, (void*)GetFingerStepSizePixels_s, NULL, NULL, bOverride);
	pClass->AddField("MinimumScreenSize", FieldType_Vector2, (void*)SetMinimumScreenSize_s, NULL, NULL, NULL, bOverride);
	return S_OK;
}